//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#pragma once

#include "Graphics/GLshell/Sampler.h"
#include "Graphics/GLshell/PixelFormat.h"

namespace Engine
{
namespace Base
{
	namespace EResource
	{
		const type	_GRAPHICS_BASE	= (type) ( int(GRAPHICS) | int(0x01000000) );
		const type	TEXTURE			= (type) ( int(_GRAPHICS_BASE) + 1 );
		const type	MATERIAL		= (type) ( int(_GRAPHICS_BASE) + 2 );
		const type	PROGRAM			= (type) ( int(_GRAPHICS_BASE) + 3 );
		const type	MODEL			= (type) ( int(_GRAPHICS_BASE) + 4 );
		const type	BITMAP_FONT		= (type) ( int(_GRAPHICS_BASE) + 5 );
		const type	VECTOR_FONT		= (type) ( int(_GRAPHICS_BASE) + 6 );
		const type	SPRITE_2D		= (type) ( int(_GRAPHICS_BASE) + 7 );
	};

}	// Base

namespace Graphics
{
	using namespace Base;
	

	class Texture;
	SHARED_POINTER( Texture );



	//
	// Texture
	//

	class Texture : public Resource
	{
		friend struct TextureLoader;

	// types
	public:
		//
		// Texture Load Parameters
		//
		struct TexLoadParams : ResLoadParams
		{
		// variables
		public:
			Sampler		sampler;
			bool		generateMipmaps;

		// methods
		public:
			TexLoadParams () : ResLoadParams( Signature() ), generateMipmaps(false)
			{}

			void CreateDefault (bool mipmaps, bool genMipmaps)
			{
				generateMipmaps = genMipmaps;

				sampler = mipmaps ? Sampler::Default2D() : Sampler::Default3D();
			}

			static signature_t Signature ()
			{
				return CompileTime::TSignature8< 'G', 'T', 'E', 'X' >::value;
			}
		};

	private:
		typedef StateManager::BindedTexture		binded_state_t;


	// variables
	protected:
		binded_state_t			_state;
		Sampler					_sampler;
		uvec3					_dim;
		bitfield32_t			_levels;	// 1 - level allocated
		EPixelFormat::type		_format;

		
	// methods
	private:
		// 2D
		bool _SetImage2D (const uvec2 &dim, EPixelFormat::type format, uint level, const uni_binbuffer_t &data);
		bool _SetSubImage2D (const uvec2 &offset, const uvec2 &dim, uint level, EPixelFormat::type format, const uni_binbuffer_t &data);
		bool _Storage2D (const uvec2 &dim, EPixelFormat::type format, uint levels);
		bool _AllocLevel2D (uint level);
		// Cube
		bool _SetImageCube (const uvec2 &dim, uint face, EPixelFormat::type format, uint level, const uni_binbuffer_t &data);
		bool _SetSubImageCube (const uvec2 &offset, const uvec2 &dim, uint face, uint level, EPixelFormat::type format, const uni_binbuffer_t &data);
		bool _StorageCube (const uvec2 &dim, EPixelFormat::type format, uint levels);
		bool _AllocLevelCube (uint level);

		bool _Create();
		void _Destroy();
		void _BindOrActive ();

	public:
		explicit
		Texture (const EngineSubSystemsRef ess, gl_texture::type target = gl_texture::TEXTURE_2D);
		~Texture ();

		void Destroy ();

		void Bind (uint unit);
		bool IsBinded () const;
		bool IsActive () const;

		bool SetImage (const uvec3 &dim, EPixelFormat::type format, uint level, const uni_binbuffer_t &data = uni_binbuffer_t());
		bool AddImage (const uvec3 &offset, const uvec3 &dim, EPixelFormat::type format, uint level, const uni_binbuffer_t &data);
		bool Alloc (const uvec3 &dim, EPixelFormat::type format, uint levels = 1);
		bool AllocLevel (uint level);

		void SetSampler (const Sampler &sampler);

		bool GenerateMipmaps ();

		bool				HasLevel (uint level)	const	{ return _levels.Get( level ); }
		uint				MaxLevels ()			const	{ return UXMathExt::GetNumberOfMipmaps( _dim ) + 1; }
		bool				HasAllMipmaps ()		const	{ return (uint)_levels == (1 << MaxLevels())-1; }
		uvec3 const &		Dimension ()			const	{ return _dim; }
		EPixelFormat::type	PixelFormat ()			const	{ return _format; }
		gl_texture::type	Target ()				const	{ return (gl_texture::type) _state.target; }
		gles::GLuint		Id ()					const	{ return _state.id; }
		uint				Unit ()					const	{ return _state.Unit(); }
		Sampler const &		GetSampler ()			const	{ return _sampler; }
		

		// Resource //
		virtual bool SwapData (const ResourcePtr &) override;
		
		virtual bool IsLoadedWithParams (const ResLoadParams &params) const override;
		
		virtual bool IsValid () const override;

		static TexturePtr	New (const EngineSubSystemsRef ess, gl_texture::type target = gl_texture::TEXTURE_2D);

		static TexLoadParams	GetDefaultLoadParams ();
	};

	

/*
=================================================
	Bind
=================================================
*/
	inline void Texture::Bind (uint unit)
	{
		//ASSERT( _state.id != 0 );
		ESS()->GetGLStateManager()->BindTexture( _state, unit );
	}
	
/*
=================================================
	IsBinded
=================================================
*/
	inline bool Texture::IsBinded () const
	{
		return ESS()->GetGLStateManager()->IsTextureBinded( _state );
	}
	
/*
=================================================
	IsActive
=================================================
*/
	inline bool Texture::IsActive () const
	{
		return IsBinded() and ESS()->GetGLStateManager()->GetActiveTextureUnit() == Unit();
	}
	
/*
=================================================
	SetSampler
=================================================
*/
	inline void Texture::SetSampler (const Sampler &sampler)
	{
		ASSERT( (sampler.NeedMipmaps() and HasAllMipmaps()) or not sampler.NeedMipmaps() );

		_BindOrActive();
		_sampler.SetAndBind( Target(), sampler );
	}
	
/*
=================================================
	_BindOrActive
=================================================
*/
	inline void Texture::_BindOrActive ()
	{
		bool	binded = IsBinded();
		bool	active = ESS()->GetGLStateManager()->GetActiveTextureUnit() == Unit();

		if ( binded and active )
			return;

		if ( not binded )
		{
			Bind( 0 );
			return;
		}

		if ( not active )
		{
			ESS()->GetGLStateManager()->ActiveTextureUnit( Unit() );
			return;
		}
	}

}	// Graphics
}	// Engine
